// *** LICENSE HEADER ***
// Filename: ./nodegraph.cpp
// This file is part of Nodewarz 0.1
// Nodewarz is a 3d OpenGL strategy game
// Copyright (C) 2007 CZestmyr
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// *** END LICENSE HEADER ***
#include "nodegraph.h"

GLuint nodeGraph::circleTexture = 0;

nodeGraph::nodeGraph(player* newParent) {
    first = 0;
    parent = newParent;
    yetUnused = true;
}

nodeGraph::~nodeGraph() {
    node* it = first;
    node* helper;
    while (it != 0) {
        helper = it;
        it = it->next;
        delete helper;
    }
}

player* nodeGraph::GetParent() {
    return parent;
}

void nodeGraph::SetLightManager(lightManager* newManager) {
    theLightManager = newManager;
}

lightManager* nodeGraph::GetLightManager() {
    return theLightManager;
}

node* nodeGraph::AddNode(float posX, float posY) {
    if (yetUnused) yetUnused = false;
    first = new node(first, this, posX, posY);
    return first;
}

void nodeGraph::RemoveNode(node* toRemove) {
    node* helper;
    if (toRemove == 0 || first == 0) return;
    if (toRemove == first) {
        helper = first;
        first = first->next;
        delete helper;
        return;
    }
    node* it = first;
    while (it != 0 && it->next != toRemove)
        it = it->next;
    if (it->next == toRemove) {
        helper = it->next;
        it->next = it->next->next;
        delete helper;
    }
}

node* nodeGraph::GetClosestNode(float posX, float posY) {
    node* ret = 0;
    float closest = 0;
    node* it = first;
    while (it != 0) {
        if (ret == 0 || it->CalculateDistanceTo(posX, posY) < closest) {
            closest = it->CalculateDistanceTo(posX, posY);
            ret = it;
        }
        it = it->next;
    }
    return ret;
}

void nodeGraph::ResetGrowthData(){
    node* it = first;
    while (it != 0) {
        for (int i = 0; i < 5; ++i){
            it->growthAmmount[i] = 0;
            it->growthWave[i] = 0;
        }
        it = it->next;
    }
}

void nodeGraph::Grow() {
    std::deque<node*> bfsQueue;
    node* it;

    //For each of the resource types, set its growth data
    for (int i = 0; i < 5; ++i){
        bfsQueue.clear();
        //Add the first-wave nodes to the queue
        it = first;
        while (it != 0) {
            if (it->growthWave[i] == 1){
                bfsQueue.push_back(it);
            }
            it = it->next;
        }
        //Perform the BFS
        while (!bfsQueue.empty()){
            it = bfsQueue.front();
            bfsQueue.pop_front();

            std::deque<node*> nextWave;
            int num = 0;
            int ammount = it->growthAmmount[i];

            //Credits don't propagate
            if (i != RT_CREDITS) {

            //Add the neighbours to the queue (if not there yet)
            connNode* conit = it->connections;
            while (conit != 0){
                //Add nodes that are not in bfs queue yet
                if (conit->connection->growthWave[i] == 0){
                    nextWave.push_back(conit->connection);
                    ++num;
                //The nodes in the same wave will also receive some of the resources from this node
                } else if (conit->connection->growthWave[i] - 1 == it->growthWave[i]){
                    nextWave.push_back(conit->connection);
                    ++num;
                }
                conit = conit->next;
            }

            //Now we know the number of nodes that will receive the resources.
            //Let's give them the resources 
            for (int j = 0; j < num; ++j){
                int resnum = -1; //-1 is a constant decay
                resnum += ammount / num;
                if (ammount % num > j)
                    resnum += 1;
                //If the resource number exceeded the decay threshold
                if (resnum > 0) {
                    nextWave[j]->growthAmmount[i] += resnum;
                    //This node was given resources, so it may propagate further
                    if (nextWave[j]->growthWave[i] != it->growthWave[i] + 1){
                        bfsQueue.push_back(nextWave[j]);
                        nextWave[j]->growthWave[i] = it->growthWave[i] + 1;
                    }
                }
            }

            } //End if (i != RT_CREDITS)

            //This node has performed all the needed tasks. We will now increase the energies or credits
            //according to the resource type
            switch (i){
                case RT_ATTACK:
                    it->attack += ammount; Constrainti(it->attack, 0, 100);
                break;
                case RT_DEFENCE:
                    it->defence += ammount; Constrainti(it->defence, 0, 100);
                break;
                case RT_LIVES:
                    it->thetaEnergy += ammount; Constrainti(it->thetaEnergy, 0, 100);
                break;
                case RT_CREDITS:
                    parent->AddCredits(ammount);
                break;
                case RT_TAKEOVER:
                    it->takeover += ammount; Constrainti(it->takeover, 0, 100);
                break;
            }
        }
    }

    //Grow the nodes, according to number of connections
    it = first;
    while (it != 0) {
        it->Grow();
        it = it->next;
    }
}

void nodeGraph::ResetLights(){
    node* it = first;
    while (it != 0) {
        it->ResetLight();
        it = it->next;
    }
}

void nodeGraph::Draw(bool showBoundaries, bool friendly, bool showEnergies) {
    node* it = first;
    while (it != 0) {
        it->Draw(showEnergies);
        it = it->next;
    }

    if (showBoundaries){
        glEnable(GL_BLEND);
        glDisable(GL_DEPTH_TEST);
        glBlendFunc(GL_ONE, GL_ONE);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, circleTexture);
        SDL_Color c = parent->GetColor();
        c.r /= 8;
        c.g /= 8;
        c.b /= 8;
        glColor3ub(c.r, c.g, c.b);
        it = first;
        while (it != 0) {
            it->DrawBoundary(friendly);
            it = it->next;
        }
    }
}

bool nodeGraph::IsEmpty(){
    if (!first && !yetUnused) return true;
    else return false;
}

void nodeGraph::LoadTextures(){
    circleTexture = ::LoadTexture("circle.png");
}

void nodeGraph::UnloadTextures(){
    ::UnloadTexture(circleTexture);
}
